<?php
namespace UserSystem;

use UserSystem\ApiMsg;
use UserSystem\Driver\DB\Mysql;
use \PDO;
use \PDOException;
use UserSystem\Logger;
use UserSystem\View;

/**
 * @author wangxi
 * 用户处理类
 * @modi 15-12-1
 */
class Account
{
    protected $userName;
    protected $userPass;

    //客户端user agent
    protected $userAgent;

    //请求端ip
    protected $ip;

    //访问令牌
    protected $accessToken;

    //token生存期
    protected $tokenExpire;

    //平台名
    protected $platform;

    //数据库连接句柄
    protected $db;

    //接口信息处理句柄
    public $msg;

    //TODO:允许访问用户系统的平台，暂时写死
    private static $_allowAccessPlatform = array(
        'www.unibox.com.cn' => 1,
        'mall.unibox.com.cn'  => 2,
        //vagrant test server
        'v2.unibox.dev'=> 3  ,
        'account.unibox.dev'=>4,
        'account.dev.unibox.com.cn'=> 5,

        //local test
        'account.unibox.dev' => 6
    );

    public function __construct($user_name = '', $user_pass = '', $token = '')
    {
        $this->userName = $user_name;
        $this->userPass = $user_pass;
        $this->accessToken = $token;

        //设置token生存期为10分钟
        $this->tokenExpire = 60 * 10;

        $this->msg=new ApiMsg();

        //TODO: need optimize
        $this->userAgent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';

        $this->ip = get_user_ip();

        //获取数据库连接句柄
        //TODO: 采用策略模式，不局限于Mysql
        $this->db = Mysql::getInstance();
    }

    public function setName($name)
    {
        $this->userName = $name;
    }

    public function setPassword($pwd)
    {
        $this->userPass = $pwd;
    }

    public function setToken($token)
    {
        $this->accessToken = $token;
    }

    /**
     * 处理登录逻辑
     */
    public function login()
    {
        if (empty($this->userName) || empty($this->userPass) || empty($this->accessToken)) {
            $this->msg->set(ApiMsg::BAD_REQUEST)->output();
        }

        if(!$this->_validAccessToken()){
            //token失效
            $this->msg->set(ApiMsg::ACCESS_TOKEN_ERROR)->output();
        }

        //根据用户名获取uid, salt, password
        $stmt_user = $this->db->prepare("select uid,salt,password from user where name_hash = :name_hash");
        $stmt_user->execute(array('name_hash' => md5($this->userName)));
        $query_user = $stmt_user->fetch(PDO::FETCH_ASSOC);

        if (is_array($query_user) && $query_user['uid'] > 0) {
            $encrypt_pass = md5($this->userPass . $query_user['salt']);

            if ($encrypt_pass === $query_user['password']) {
//                $this->db->setAttribute(PDO::ATTR_AUTOCOMMIT, false);
//                $this->db->beginTransaction();

                $stmt=$this->db->prepare("update `user` set `last_login`=:last_login where `uid`=:uid");
                $stmt->execute(array('last_login'=>time(), 'uid'=>$query_user['uid']));

                $aff_row=$this->db->exec("update `user` set `count_login`=`count_login`+1 where `uid`=".$query_user['uid']);

//                $this->db->commit();

                if($aff_row){
                    $this->msg->set(ApiMsg::LOGIN_OK)->set('success',true);
                }
                else{
                    $this->msg->set('info','内部错误：PDO写入失败');
                }

            }
            else {
                $this->msg->set(ApiMsg::PWD_ERROR);
            }

            //save login log
            Logger::save($this->msg->get('info'), $query_user['uid']);

        }
        else {
            $this->msg->set(ApiMsg::USER_NOT_EXIST);
        }

        $this->msg->output();
    }

    /**
     * 处理注册逻辑
     */
    public function register()
    {
        if (empty($this->userName) || empty($this->userPass) || empty($this->accessToken)) {
            $this->msg->set(ApiMsg::BAD_REQUEST)->output();
        }

        //检查用户名
        if (!$this->_validUserName($this->userName)) {
            $this->msg->set(ApiMsg::INVALID_USER_NAME)->output();
        }
        //检查密码
        if (!$this->_validUserPwd($this->userPass)) {
            $this->msg->set(ApiMsg::INVALID_USER_PASS)->output();
        }
        //检查请求来自的子系统
        if (!$this->_checkPlatform()) {
            $this->msg->set(ApiMsg::INVALID_PLATFORM)->output();
        }

        if(!$this->_validAccessToken()){
            $this->msg->set(ApiMsg::ACCESS_TOKEN_ERROR)->output();
        }

        //用户名hash
        $name_hash = md5($this->userName);

        //检查库中是否已注册该用户
        $stmt_user_reg = $this->db->prepare("select `uid` from `user` where name_hash = :name_hash");
        $stmt_user_reg->execute(array('name_hash' => $name_hash));
        $is_reg = $stmt_user_reg->rowCount() > 0 ? true : false;
        if ($is_reg) {
            $this->msg->set(ApiMsg::USER_ALREADY_EXIST)->output();
        }

        //根据当前注册时间和用户名生成动态盐
        $reg_time = time();
        $salt_key = substr(md5($this->userName . $reg_time), 0, 10);

        //生成密码
        $user_pass = md5($this->userPass . $salt_key);

        $insert_user = "insert into `user` (`username`, `platform_id`, `name_hash`, `password`,
                    `salt`,`register_time`)
                    values(:user_name, :platform_id, :name_hash, :user_pass, :salt, :reg_time)";

        $stmt = $this->db->prepare($insert_user);
        $stmt->execute(array(
            'user_name'   => $this->userName,
            'platform_id' => self::$_allowAccessPlatform[$this->platform],
            'name_hash'   => $name_hash,
            'user_pass'   => $user_pass,
            'salt'        => $salt_key,
            'reg_time'    => $reg_time
        ));

        if (($insert_id=$this->db->lastInsertId()) > 0) {
            $this->msg->set(ApiMsg::REGISTER_OK)->set('success',true);

            //save register log
            Logger::save($this->msg->get('info'), $insert_id);
        }
        else {
            $this->msg->set(ApiMsg::REGISTER_FAILED);
        }

        $this->msg->output();

    }

    public function logout()
    {
        //
        $this->msg->output();
    }

    public function genAccessToken()
    {
        if(!$this->_checkPlatform()){
            $this->msg->set(ApiMsg::INVALID_PLATFORM);
            return '';
        }

        //token的查询键 USER_AGENT, IP
        $token_key = md5($this->userAgent . $this->ip);

        //判断数据库中是否保存此token
        $stmt_token = $this->db->prepare("select * from token where token_key = :token_key");
        $stmt_token->execute(array('token_key' => $token_key));
        $query_token = $stmt_token->fetch(PDO::FETCH_ASSOC);

        if ($stmt_token->rowCount() > 0) {
            //抛出token值
            $token_str = $query_token['token_value'];
        }
        else {
            //写入新的token
            $token_str = md5($this->userAgent . $this->ip . time());
            $insert_token = $this->db->prepare("insert into token (token_key, token_value, expire)
												values (:token_key, :token_value, :token_expire)");
            $insert_token->execute(array(
                'token_key'    => $token_key,
                'token_value'  => $token_str,
                'token_expire' => time() + $this->tokenExpire
            ));
        }

        return $token_str;
    }

    //检查http请求的平台来源
    private function _checkPlatform()
    {
        $refer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';

        //TODO:测试refer, 部署时需要删掉该行
        $refer = 'http://account.unibox.dev';

        //根据 http头部的 refer区分请求来自什么平台
        if (!empty($refer)) {

            if (preg_match('/https?:\/\/((\w+\.){2}(\w+)(\.\w+){0,2})\/?/', $refer, $match)) {
                $refer = $match[1];

                if (!in_array($refer, array_keys(self::$_allowAccessPlatform))) {
                    return false;
                }
                else {
                    $this->platform = $refer;
                    return true;
                }
            }

        }

        return false;
    }

    private function _validAccessToken()
    {
        if (empty($this->accessToken) || strlen($this->accessToken) !== 32) {
            return false;
        }

        $token_key = md5($this->userAgent . $this->ip);

        //判断数据库中是否保存此token
        $stmt_token = $this->db->prepare("select * from token where token_key = :token_key");
        $stmt_token->execute(array(
            'token_key' => $token_key
        ));
        $query_token = $stmt_token->fetch(PDO::FETCH_ASSOC);

        if ($query_token !== false && count($query_token) > 0) {
            if ($this->accessToken == $query_token['token_value']) {
                //判断是否过期
                if (intval($query_token['expire']) < time()) {
                    //删除token
                    $del_token = $this->db->prepare("delete from token where token_key = :token_key");
                    $del_token->execute(array('token_key' => $token_key));

                    return false;

                }
                else {
                    return true;
                }
            }
        }

        return false;
    }

    //校验用户名格式
    private function _validUserName($name)
    {
        if (preg_match('/^([\w_]+)@[\w_]+(\.[\w_]+){1,2}$/i', $name) || preg_match('/^1\d{10}$/i', $name)) {
            return true;
        }

        return false;
    }

    //校验密码格式
    private function _validUserPwd($pwd)
    {
        if (preg_match('/^[a-z0-9_]{6,20}$/i', $pwd)) {
            return true;
        }

        return false;
    }

}